#include "matrix.h"
#include <assert.h>
#include "mtm.h"
#include <stdarg.h>
#include "mtmtest.h"

Matrix initMatrix( int rows, int columns, ... )
{
	Matrix result( rows, columns );

	va_list vl;
	va_start( vl, columns );

	for( int r = 1; r <= rows; ++r )
	{
		for( int c = 1; c <= columns; ++c )
		{
			double cell = va_arg(vl,double);
			result(r, c) = cell;
		}
	}

	va_end( vl );

	return result;
}

bool matrixTest()
{
	// Unit Matrix
	Matrix I = Matrix::createUnit(3);

	// 3x4 non-zero matrix
	Matrix A = initMatrix(3,4, 
		1.0, 2.0, 3.0, 4.0,
		5.0, 6.0, 7.0, 8.0,
		9.0, 10.0, 11.0, 12.0);
	
	// 3x4 zero matrix
	Matrix B(3,4);

	// Accessing matrix cells
	for (int r = 1; r <= A.rows(); r++)
	{
		for (int c = 1; c <= A.columns(); c++)
		{
			ASSERT_EQUALS(true, IS_DOUBLE_EQUAL(A(r,c), (r - 1)* 4 + c));
			ASSERT_EQUALS(true, IS_DOUBLE_EQUAL(B(r,c), 0.0));
		}
	}

	for (int r = 1; r <= I.rows(); r++)
	{
		for (int c = 1; c <= I.columns(); c++)
		{
			ASSERT_EQUALS(true, IS_DOUBLE_EQUAL(I(r,c),r == c ? 1.0 : 0.0));
		}
	}

	// Matrix addition, substraction & negation
	
	Matrix C = B;
	ASSERT_NO_THROW(B += -A);
	ASSERT_NO_THROW(B = A - B);
	ASSERT_NO_THROW(B -= A);
	ASSERT_NO_THROW(B = A + B);

	// Matrix comparison
	ASSERT_EQUALS(A*2-C, B);

	// Matrix multiplication
	ASSERT_NO_THROW(C = I * B);
	ASSERT_NO_THROW(I *= C);

	// accessing rows & columns properties
	ASSERT_EQUALS(3,I.rows());
	ASSERT_EQUALS(4,I.columns());

	// Multiplying matrix by scalar
	ASSERT_NO_THROW(I *= 5.0);
	ASSERT_NO_THROW(A = B + I * 2.0);

	// Matrix comparison
	ASSERT_EQUALS(true,A != B);
	ASSERT_EQUALS(B,B);

	// Matrix transpose & inverse
	Matrix I4 = Matrix::createUnit(4);
	ASSERT_EQUALS(I4.transpose(),I4.inverse());

	ASSERT_THROW(MatrixExceptions::IndexOutOfRange, I4(5,2));
	ASSERT_THROW(MatrixExceptions::BadDimensions, A.inverse());
	ASSERT_NO_THROW(I4(1,1) = 0.0);
	ASSERT_THROW(MatrixExceptions::Singular, I4.inverse());
	return true;
}

int main()
{
	RUN_TEST(matrixTest);
	return 0;
}
